#include "processor.h"

trace_queue::trace_queue(unsigned int size) {
	tq = new trace_queue_entry_t[size];
	this->size = size;
	head = 0;
	tail = 0;
	current_length = 0;
}


// push a trace index(payload buffer index of first istruction) into the trace queue
unsigned int trace_queue::push(unsigned int trace_id, unsigned int trace_length, unsigned int processing_element, unsigned int start_pc) {
	assert(current_length < size);
	int tail_val=tail;
	tq[tail].trace_id= trace_id;
	tq[tail].trace_length = trace_length;
	tq[tail].completed_instructions = 0;
	tq[tail].processing_element = processing_element;
	tq[tail].starting_pc = start_pc;
	tq[tail].valid = true;
	tq[tail].exception = false;
	tq[tail].exception_index = -1;
	tq[tail].exception_pc = -1;
	tail = MOD_S((tail + 1), size);
	current_length += 1;

	return tail_val;
}


// read a trace's index from the trace queue
unsigned int trace_queue::read_head_trace_id() {
	assert(current_length > 0);
	return(tq[head].trace_id);
}


// pop a trace's length from the trace queue
unsigned int trace_queue::read_head_trace_length() {
	assert(current_length > 0);
	return(tq[head].trace_length);
}


// pop an instructions (its payload buffer index) from the trace queue
void trace_queue::pop() {

	assert(current_length > 0);
	head = MOD_S((head + 1), size);
	current_length -= 1;
}

// flush the trace queue (make it empty)
void trace_queue::flush() {
	head = 0;
	tail = 0;
	current_length = 0;
}

bool trace_queue::trace_queue_empty(){
	if(head==tail)
		return true;
	else
		return false;
}

bool trace_queue::trace_queue_full(){
	return (current_length==size);
}

unsigned int trace_queue::get_head_processing_element()
{
	assert(current_length>0);
	return tq[head].processing_element;
}

unsigned int trace_queue::get_starting_PC(unsigned int ID)
{
	assert(current_length>0);
	for(int i=head; i<head+current_length; i++){
		if(tq[i%size].processing_element == ID)
			return tq[i%size].starting_pc;
	}
	assert(0);
	return 0;
}

void trace_queue::inc_completed_inst(unsigned int ID)
{
	assert(current_length>0);
	for(int i=head; i<head+current_length; i++){
		if(tq[i%size].processing_element == ID)
		{
			tq[i%size].completed_instructions ++;
			assert(tq[i%size].completed_instructions <= tq[i%size].trace_length);
			break;
		}
	}
	assert(0);

}

void trace_queue::clear_valid(unsigned int ID)
{
	assert(current_length>0);
	for(int i=head; i<head+current_length; i++){
		if(tq[i%size].processing_element == ID)
		{
			tq[i%size].valid = 0;
			assert(tq[i%size].completed_instructions <= tq[i%size].trace_length);
			break;
		}
	}
	assert(0);

}


unsigned int trace_queue::get_pay_index(unsigned ID)
{
	assert(current_length>0);
	for(int i=head; i<head+current_length; i++){
		if(tq[i%size].processing_element == ID)
			return tq[i%size].trace_id;
	}
	assert(0);
	return 0;
}
bool trace_queue::free_if_last_PE(unsigned int ID)
{
	if(tq[(tail-1)%size].processing_element == ID)
	{
		tail = MOD_S((tail - 1), size);
		return true;
	}
	else return false;
}

bool trace_queue::head_trace_complete()
{
	assert(current_length>0);
	return (tq[head].completed_instructions == tq[head].trace_length);
}

void trace_queue::set_exception(unsigned int ID, unsigned int PC, unsigned int except_ind)
{
	assert(current_length>0);
	for(int i=head; i<head+current_length; i++){
		if(tq[i%size].processing_element == ID && (tq[i%size].exception == false))
		{
			tq[i%size].exception_pc = PC;
			tq[i%size].exception = true;
			tq[i%size].exception_index = except_ind;
			break;
		}
	}
	assert(0);
}
bool trace_queue::is_head_exception()
{
	assert(current_length>0);
	return (tq[head].exception);
}
unsigned int trace_queue::get_exception_PC()
{
	assert(current_length>0);
	return (tq[head].exception_pc);
}
unsigned int trace_queue::get_exception_index()
{
	assert(current_length>0);
	return (tq[head].exception_index);
}

